package org.com.cis.cf;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import org.com.cis.cf.util.SortTrainByPredicted;
import org.com.cis.cf.util.TrainPoint;

public class LBDMF extends MF{
	
	void iterate( double momentum )
	{
		int user, item, jtem, irate, jrate, f, position_i, position_j;
		double x_ui, x_uj, x_uij, gradient, weight;
		for ( user = 1; user <= userNumber; ++user) {
			int trainSize = trainSet.get(user).size();
			
			ArrayList<TrainPoint> trainList = new ArrayList<TrainPoint>();
			for (int x = 0; x != trainSize; ++x) {
				item = trainSet.get(user).get(x) / 10;
				irate = trainSet.get(user).get(x) % 10;
				x_ui = predict(user, item);
				trainList.add(new TrainPoint(item, irate, x_ui));
			}
			
			@SuppressWarnings("unchecked")
			Comparator<TrainPoint> sortByPredicted = new SortTrainByPredicted();
			Collections.sort(trainList, sortByPredicted);
			
			int [] itemRankIndex = new int[itemNumber+1];
			for ( int i = 0; i != trainSize; ++i) {
				itemRankIndex[trainList.get(i).getItem()] = i+1;
			}
			
			
			for ( int i = 0; i != trainSize; ++i) {
				item = trainSet.get(user).get(i) / 10;
				irate = trainSet.get(user).get(i) % 10;
				x_ui = predict(user, item);
				position_i = itemRankIndex[item];
				
				for ( int j = 0; j != trainSize; ++j) {
					jtem = trainSet.get(user).get(j) / 10;
					jrate = trainSet.get(user).get(j) % 10;
					
					if (irate > jrate) {
						
						x_uj = predict(user, jtem);
						position_j = itemRankIndex[jtem];
						x_uij = x_ui - x_uj;
						gradient = 1 / (1+Math.exp(x_uij));
						weight = (Math.pow(2, irate)-Math.pow(2, jrate) )  * (1/(Math.log(1+position_i))-1/Math.log(1+position_j)) / trainIDCG[user];
						weight = Math.abs(weight);
						gradient =  weight * gradient;
						gradient *= (1-momentum);
						
						for( f = 0;  f != factorNumber; ++f){
							
							double u_f = userFactor.getQuick(user, f);
							double i_f = itemFactor.getQuick(item, f);
							double j_f = itemFactor.getQuick(jtem, f);

							double delta_u = gradient * (i_f - j_f) + regularization * u_f;
							userFactor.setQuick(user, f, userFactor.getQuick(user, f) + learnRate * delta_u);

							double delta_i = gradient * u_f + regularization * i_f;
							itemFactor.setQuick(item, f, itemFactor.getQuick(item, f) + learnRate * delta_i);
							
							double delta_j = gradient * (-u_f) + regularization * j_f;
							itemFactor.setQuick(jtem, f, itemFactor.getQuick(jtem, f) + learnRate * delta_j);
							
							
						}	
						
						
						double err = irate- predict(user, item);
						
						for( f = 0;  f != factorNumber; ++f){
							double u_f = userFactor.getQuick(user, f);
							double i_f = itemFactor.getQuick(item, f);

							double delta_u = err * i_f - regularization * u_f;
							userFactor.setQuick(user, f, userFactor.getQuick(user, f) + learnRate * delta_u);

							double delta_i = err * u_f - regularization * i_f;
							itemFactor.setQuick(item, f, itemFactor.getQuick(item, f) + learnRate * delta_i);
						}
						
						err = jrate- predict(user, jtem);
						
						for( f = 0;  f != factorNumber; ++f){
							double u_f = userFactor.getQuick(user, f);
							double j_f = itemFactor.getQuick(jtem, f);

							double delta_u = err * j_f - regularization * u_f;
							userFactor.setQuick(user, f, userFactor.getQuick(user, f) + learnRate * delta_u);

							double delta_j = err * u_f - regularization * j_f;
							itemFactor.setQuick(jtem, f, itemFactor.getQuick(jtem, f) + learnRate * delta_j);
						}
					}
				}
				
			}
		}
	}
	
	
	/**
	 * get item position from rate for a user
	 * @param user
	 * @param rate
	 * @return
	 */
	int getItemPositionTrain(int user, int rate) {
		int pos = 1;
		switch(rate) {
		case 5:
			pos = 1;
			break;
		case 4:
			pos = 1 + trainDist[user][0];
			break;
		case 3:
			pos = 1 + trainDist[user][0] + trainDist[user][1];
			break;
		case 2:
			pos = 1 + trainDist[user][0] + trainDist[user][1] + trainDist[user][2];
			break;
		case 1:
			pos = 1 + trainDist[user][0] + trainDist[user][1] + trainDist[user][2] + trainDist[user][3];
			break;
		default:
			System.err.println("get positon error!!");
			pos = -1;
			break;
		}
		return pos;
	}
	
	public void runModel( double momentum )
	{
		calcTrainIDCG();
		
		for ( int r = 1; r <= maxIteration; ++r) {
			iterate(momentum);
			//ArrayList<Double> trainNdcg = calcNdcg(trainSet);
			double testNdcg1 = calcNdcg(testSet, 1, true);
			double testNdcg2 = calcNdcg(testSet, 2, true);
			double testNdcg3 = calcNdcg(testSet, 3, true);
			double testNdcg4 = calcNdcg(testSet, 4, true);
			double testNdcg5 = calcNdcg(testSet, 5, true);
			double testNdcg10 = calcNdcg(testSet, 10, true);
			double trainNdcg10 = calcNdcg(trainSet, 10, true);
			System.out.print("iteration: " + r);
			NumberFormat nFormat=NumberFormat.getNumberInstance(); 
		    nFormat.setMaximumFractionDigits(4);
		    
		    System.out.print("\t" + nFormat.format(testNdcg1) + "  " + 
		    		nFormat.format(testNdcg2) + "  " + 
		    		nFormat.format(testNdcg3) + "  " + 
		    		nFormat.format(testNdcg4) + "  " + 
		    		nFormat.format(testNdcg5) + "  " + 
		    		nFormat.format(testNdcg10) + "  " +
		    		nFormat.format(trainNdcg10));
		    
		    System.out.println();
/*			testNdcg1 = calcNdcg(trainSet, 1, true);
			testNdcg2 = calcNdcg(trainSet, 2, true);
			testNdcg3 = calcNdcg(trainSet, 3, true);
			testNdcg4 = calcNdcg(trainSet, 4, true);
			testNdcg5 = calcNdcg(trainSet, 5, true);
			testNdcg = calcNdcg(trainSet, Integer.MAX_VALUE, false);

		    System.out.println("\t\t" + nFormat.format(testNdcg1) + "  " + 
		    		nFormat.format(testNdcg2) + "  " + 
		    		nFormat.format(testNdcg3) + "  " + 
		    		nFormat.format(testNdcg4) + "  " + 
		    		nFormat.format(testNdcg5) + "  " + 
		    		nFormat.format(testNdcg));*/
		    
/*			for ( int i = 0; i != testNdcg.size(); ++i) {
				System.out.print(nFormat.format(testNdcg.get(i)) + "\t");
			}
			System.out.println("\n");*/
		}
	}

}
